//The MIT License (MIT)
//
//Copyright (c) 2018 dote
//
//Permission is hereby granted, free of charge, to any person obtaining a copy of
//this software and associated documentation files (the "Software"), to deal in
//the Software without restriction, including without limitation the rights to
//use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
//the Software, and to permit persons to whom the Software is furnished to do so,
//subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
//FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
//COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
//IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
//CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package com.github.dote.mio.event;

import com.github.dote.mio.client.IOClient;
import com.github.dote.mio.protocal.Packet;
import com.github.dote.mio.protocal.VoidData;
import com.github.dote.mio.util.JsonUtil;
import com.github.dote.mio.util.ReflectionUtil;
import io.netty.util.internal.PlatformDependent;

import java.util.Iterator;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

public class EventManager {

    class EventEntry {

        private Queue<EventListener> eventQueue = new ConcurrentLinkedQueue();

        public Queue<EventListener> getQueue() {
            return eventQueue;
        }

        public void addEvent(EventListener eventListener) {
            eventQueue.add(eventListener);
        }

        public void clear() {
            eventQueue.clear();
        }
    }

    private ConnectionListener connectionListener = null;
    private DisconnectionListener disconnectionListener = null;
    private LostPacketListener lostPacketListener = null;
    private OfflinePacketListener offlinePacketListener=null;

    private Map<String, EventEntry> eventEntrys = PlatformDependent.newConcurrentHashMap();

    public EventManager() {
    }

    public void addConnectionListener(ConnectionListener connectionListener){
        this.connectionListener=connectionListener;
    }

    public void onConnect(IOClient client) {
        if (connectionListener != null)
            connectionListener.onConnect(client);
    }

    public void addDisconnectionListener(DisconnectionListener disconnectionListener){
        this.disconnectionListener=disconnectionListener;
    }

    public void onDisconnect(IOClient client) {
        if (disconnectionListener != null)
            disconnectionListener.onDisconnect(client);
    }

    public void addLostPacketListener(LostPacketListener lostPacketListener){
        this.lostPacketListener=lostPacketListener;
    }

    public void onLostPacket(IOClient client, Packet packet,String fromSessionId,String toSessionId) {
        if (lostPacketListener != null)
            lostPacketListener.onLostPacket(client, packet,fromSessionId,toSessionId);
    }

    public void addOfflinePakcetListener(OfflinePacketListener offlinePacketListener){
        this.offlinePacketListener=offlinePacketListener;
    }

    public boolean onOfflinePacket(IOClient client, Packet packet) {
        if (offlinePacketListener != null)
            return offlinePacketListener.onOfflinePacket(client, packet);
        return false;
    }



    public void addEventListener(String eventName, EventListener eventListener) {
        if (!eventEntrys.containsKey(eventName)) {
            EventEntry eventEntry = new EventEntry();
            eventEntrys.put(eventName, eventEntry);
        }
        EventEntry ee = eventEntrys.get(eventName);
        ee.addEvent(eventListener);
    }

    public void removeEventListener(String eventName) {
        if (eventEntrys.containsKey(eventName)) {
            EventEntry eventEntry = new EventEntry();
            eventEntry.clear();
        }
    }

    public void onEvent(IOClient client, String eventName, String dataOfJsonString) {
        if (eventName == null || "".equals(eventName))
            return;
        if (dataOfJsonString == null || "".equals(dataOfJsonString))
            return;
        if (eventEntrys.containsKey(eventName)) {
            EventEntry eventEntry = eventEntrys.get(eventName);
            Queue<EventListener> eeQueue = eventEntry.getQueue();
            Iterator iterable = eeQueue.iterator();
            while (iterable.hasNext()) {
                EventListener listener = (EventListener) iterable.next();
                Object data = getEventData(dataOfJsonString, listener);
                if (data != null) {
                    listener.onEvent(client, data);
                }
            }
        }
    }

    public void onEvent(IOClient client, String eventName, Object data) {
        if (eventName == null || "".equals(eventName))
            return;
        if (data == null)
            return;
        if (eventEntrys.containsKey(eventName)) {
            EventEntry eventEntry = eventEntrys.get(eventName);
            Queue<EventListener> eeQueue = eventEntry.getQueue();
            Iterator iterable = eeQueue.iterator();
            while (iterable.hasNext()) {
                EventListener listener = (EventListener) iterable.next();
                Class clazz = getEventListenerClazz(listener);
                if (data != null && clazz == data.getClass()) {
                    listener.onEvent(client, data);
                }
            }
        }
    }

    /**
     * 获取事件值 json 转obj
     *
     * @param jsonString
     * @param dataListener
     * @return
     */
    private Object getEventData(String jsonString, EventListener<?> dataListener) {
        if (jsonString == null || "".equals(jsonString))
            return new VoidData();
        Class clazz = getEventListenerClazz(dataListener);
        return JsonUtil.parseObject(jsonString, clazz);
    }

    private Class getEventListenerClazz(EventListener<?> dataListener) {
        Class clazz = ReflectionUtil.getSuperInterfaceGenricType(dataListener.getClass(), 0, 0);
        return clazz;
    }
}
